#include "simple_trail.h"
#include "math/math_core.h"
#include "math/mat4x4.h"
#include "math/intersection.h"
#include "math/math_core.h"
#include "num/rand.h"
#include "graphics/graphics_util.h"

#include "dd/dd_man.h"
#include "util/mmgr.h"				// log
#include "util/log.h"				// log
#include "util/common_macros.h"		// macros
#include "util/timer.h"		// macros

#include <cassert>

using namespace vfx;

// used by all effects
const int					BezierDivisions = 4;

SimpleTrail::SimpleTrail(const std::string& Image, float	GenTime, float Timeout, bool additive) :
	m_NodeA(NULL),
	m_AddNode(GenTime),
	m_NodeC(0),m_LowNode(0), m_HighNode(0),
	m_pImage(0),
	m_Additive(additive)
{
	SetImage(Image);
	CreateNodes(GenTime, Timeout);
}


SimpleTrail::~SimpleTrail()
{
	UTIL_SAFE_DELETEA(m_NodeA);
}


void	SimpleTrail::SetImage(const std::string& Name)
{
	m_pImage = GetGraphics()->LoadImageFromFile(Name);
}


void	SimpleTrail::Tick(float dt)
{
	m_AddNode.Tick(dt);
	float	time = TIMER().Time();

	if (time - m_NodeA[m_LowNode].Time > m_Timeout && m_LowNode < m_HighNode)
	{
		m_LowNode++;
	}
}


void	SimpleTrail::Render(const math::Vec2& Offset, float Scale)
{
	if (!m_pImage) return;
	if (m_HighNode==m_LowNode) return;

	Graphics* pG = GetGraphics();
	const COLOR	c = 0xFFFFFFFF;
	int	VertI = 0, IndexI = 0;
	Graphics::Vertex2D*	VertA = Graphics::GetTempVert();
	unsigned short* IndexA = Graphics::GetTempIdx();
	if (m_Additive)
    {
		GetGraphics()->SetupAdditiveAlpha(true);
	} 
	pG->SetRenderState(Graphics::RS_CULL_MODE, Graphics::RV_CULL_NONE);

	float	sx = Scale * GraphicsUtil::WIDTH; 
	float	sy = Scale * GraphicsUtil::HEIGHT; 
	// rewrite as strip instead?
	float	time = TIMER().Time();
	float	InvTimeOut = 1.0f / m_Timeout;
	math::Vec2	out;
	for (int i=m_LowNode; i<m_HighNode; i++)
	{
		//Node*	pFrom = &m_NodeA[i-1];
		Node*	pNode = &m_NodeA[i];
		//Node*	pTo = &m_NodeA[i+1];
		float	Alpha = 1.0f - ((time - pNode->Time) / m_Timeout);
		COLOR	Color = ColorfRGBA(1, 1, 1, Alpha);

		// generate a bezier curve to the next layer?
/*		for (int j=0; j<BezierDivisions-1; j++)
		{
			float	t = float(j) / (float)BezierDivisions;
			out = pFrom->From + (pNode->From - pFrom->From) * t;
			MathFunc::ComputerBezier(pFrom->From, pNode->From, pTo->From, t, out);
			VertA[VertI++].Fill((out.x - Offset.x) * sx, (out.y - Offset.y) * sy, Color, 0.01f, 0.5f);


			out = pFrom->To + (pNode->To - pFrom->To) * t;
			MathFunc::ComputerBezier(pFrom->To, pNode->To, pTo->To, t, out);
			VertA[VertI++].Fill((pNode->To.x - Offset.x) * sx, (pNode->To.y - Offset.y) * sy, Color, 0.99f, 0.5f);
		}*/
		VertA[VertI++].Fill((pNode->From.x - Offset.x) * sx, (pNode->From.y - Offset.y) * sy, Color, 0.01f, 0.5f);
		VertA[VertI++].Fill((pNode->To.x - Offset.x) * sx, (pNode->To.y - Offset.y) * sy, Color, 0.99f, 0.5f);
	}

	for (int i=0; i<VertI-2; i+=2)
	{
		IndexA[IndexI++] = i;
		IndexA[IndexI++] = i+2;
		IndexA[IndexI++] = i+3;

		IndexA[IndexI++] = i;
		IndexA[IndexI++] = i+3;
		IndexA[IndexI++] = i+1;
	}

/*	dd::Manager& DD = dd::Manager::Instance();
	for (int i=0; i<IndexI-1; i++)
	{
		math::Vec2	From( VertA[IndexA[i]].x / sx, VertA[IndexA[i]].y/sy);
		math::Vec2	To( VertA[IndexA[i+1]].x / sx, VertA[IndexA[i+1]].y/sy);
		DD.AddLine2D(From, To, ColorRGBA(255, 255, 0));
	}*/

	if (IndexI > 0)
	{
		pG->SetTexture(0,m_pImage);
		pG->DrawIndexVertex2D(&VertA[0], VertI, &IndexA[0], IndexI, Graphics::PT_TRI_LIST);
	}
	if (m_Additive)
    {
		GetGraphics()->SetupAdditiveAlpha(false);
    } 
	pG->SetRenderState(Graphics::RS_CULL_MODE, Graphics::RV_CULL_CCW);
}

void	SimpleTrail::UpdatePos(const math::Vec2& From, const math::Vec2& To )
{
	if (m_AddNode.IsReady())
	{
		assert(m_HighNode < m_NodeC);
		m_NodeA[m_HighNode].From = From;
		m_NodeA[m_HighNode].To = To;
		m_NodeA[m_HighNode].Time = TIMER().Time();
		m_HighNode++;
		if (m_HighNode>=m_NodeC)
		{
			CopyNodes();
		}
		m_AddNode.Reset();

	}
}

void	SimpleTrail::Clear()
{
	m_HighNode = m_LowNode = 0;
	m_AddNode.SetLimit(m_GenTime);
}

void	SimpleTrail::CreateNodes(float	GenTime, float Timeout)
{
	UTIL_SAFE_DELETEA(m_NodeA);
	m_NodeC = (int)((1.0f / GenTime) * Timeout * 2.0f);
	m_NodeA = new Node[m_NodeC];
	m_LowNode = m_HighNode = 0;
	m_GenTime = GenTime;
	m_Timeout = Timeout;
	m_AddNode.SetLimit(m_GenTime);
}

void	SimpleTrail::CopyNodes()
{
	if (m_HighNode - m_LowNode > 0)
	{
		memcpy(&m_NodeA[0], &m_NodeA[m_LowNode], (m_HighNode - m_LowNode) * sizeof(Node));
	}
	m_HighNode = m_HighNode - m_LowNode;
	UTIL_CLAMP(m_HighNode, 0, m_NodeC-1);
	m_LowNode = 0;
}

